package com.engine.core.util;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONObject;
import org.apache.ibatis.cache.CacheKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Field;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class JsonIndex {
    private static Logger logger= LoggerFactory.getLogger(JsonIndex.class);
    private Map<CacheKey, Object> indexMapping=new LinkedHashMap<CacheKey, Object>();

    private Object rootJSON;

    public JsonIndex(String jsonStr){
        CacheKey cacheKey=new CacheKey(new Object[]{"@"});
        this.rootJSON = JSON.parse(jsonStr);
        indexMapping.put(cacheKey,rootJSON);


        if(rootJSON instanceof JSONObject){
            JSONObject currentNew=(JSONObject)rootJSON;
            currentNew.put("index__",getKeyStr(cacheKey));
            addObjectIndex(rootJSON,null,cacheKey);
        }else if(rootJSON instanceof JSONArray){
            addArrayIndex(rootJSON,null,cacheKey,cacheKey);
        }
    }

    public void addObjectIndex(Object rootJSON, Object current,CacheKey parentKey){
        if(current==null){
            current=rootJSON;
        }
        Map<String,Object> resetMap=new LinkedHashMap<>();
        if(current instanceof JSONObject){
            ((JSONObject)current).entrySet().forEach(entry->{
                try {
                    if(entry.getValue() instanceof JSONObject){
                        JSONObject source=(JSONObject)entry.getValue();
                        JSONObject currentNew=source.clone();
                        CacheKey newKey= parentKey.clone();
                        newKey.update(entry.getKey());
                        currentNew.put("index__",getKeyStr(newKey));
                        currentNew.put("parent__",getKeyStr(parentKey));
                        resetMap.put(entry.getKey(), currentNew);
                        indexMapping.put(newKey,currentNew);
                    }else if(entry.getValue() instanceof JSONArray){
                        JSONArray jsonArray=(JSONArray) entry.getValue();
                        CacheKey newKey= parentKey.clone();
                        newKey.update(entry.getKey());
                        indexMapping.put(newKey,jsonArray);
                        Map<Integer,Object> resetArrMap=new LinkedHashMap<>();
                        for (int i = 0; i < jsonArray.size(); i++) {
                            if(jsonArray.get(i) instanceof JSONObject){
                                JSONObject source=(JSONObject)jsonArray.get(i);
                                JSONObject currentNew=source.clone();
                                CacheKey newIdxKey= newKey.clone();
                                newIdxKey.update("["+i+"]");
                                currentNew.put("index__",getKeyStr(newIdxKey));
                                currentNew.put("parent__",getKeyStr(parentKey));
                                resetArrMap.put(i,currentNew);
                                indexMapping.put(newIdxKey,currentNew);
                                addObjectIndex(rootJSON, currentNew, newIdxKey);
                            }else if(jsonArray.get(i) instanceof JSONArray){
                                CacheKey newIdxKey= newKey.clone();
                                newIdxKey.update("["+i+"]");
                                indexMapping.put(newIdxKey,jsonArray.get(i));
                                addArrayIndex(rootJSON,jsonArray.get(i),newIdxKey,newIdxKey);
                            }
                        }
                        resetArrMap.entrySet().stream().forEach(s->{
                             jsonArray.set(s.getKey(),s.getValue());
                        });
                    }
                } catch (Exception e) {
                    logger.error("addObjectIndex",e);
                }
            });
            Object finalCurrent = current;
            resetMap.entrySet().forEach(s->{
                ((JSONObject) finalCurrent).put(s.getKey(),s.getValue());
            });
        }

    }

    public void addArrayIndex(Object rootJSON, Object current,CacheKey currentKey,CacheKey parentKey){
        if(current==null){
            current=rootJSON;
        }
        JSONArray jsonArray=(JSONArray) current;
        Map<Integer,Object> resetArrMap=new LinkedHashMap<>();
        try {
            for (int i = 0; i < jsonArray.size(); i++) {
                if(jsonArray.get(i) instanceof JSONObject){
                    JSONObject source=(JSONObject)jsonArray.get(i);
                    JSONObject currentNew=source.clone();
                    CacheKey newIdxKey= currentKey.clone();
                    newIdxKey.update("["+i+"]");
                    currentNew.put("index__",getKeyStr(newIdxKey));
                    currentNew.put("parent__",getKeyStr(parentKey));
                    indexMapping.put(newIdxKey,currentNew);
                    resetArrMap.put(i,currentNew);
                    addObjectIndex(rootJSON, currentNew, newIdxKey);
                }else if(jsonArray.get(i) instanceof JSONArray){
                    CacheKey newIdxKey= currentKey.clone();
                    newIdxKey.update("["+i+"]");
                    indexMapping.put(newIdxKey,jsonArray.get(i));
                    addArrayIndex(rootJSON,jsonArray.get(i),newIdxKey,parentKey);
                }
            }
            resetArrMap.entrySet().stream().forEach(s->{
                jsonArray.set(s.getKey(),s.getValue());
            });

        } catch (Exception e) {
            logger.error("addArrayIndex",e);
        }
    }
    public static String getKeyStr(CacheKey cacheKey){
        try {
            Field f=ReflectionUtils.findField(cacheKey.getClass(),"updateList");
            f.setAccessible(true);
            List<Object> updateList=(List<Object>)f.get(cacheKey);
            return updateList.stream().map(s->String.valueOf(s)).collect(Collectors.joining("/"));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    public static CacheKey getKey(String keys){
        try {
            String keyArr[]=keys.split("/");
            CacheKey cacheKey=new CacheKey(keyArr);
            return cacheKey;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    private Object removeIndex(Object node,Object current){
        if(current==null){
            current=node;
        }
        if(current instanceof JSONObject){
            Iterator<Map.Entry<String,Object>> iteratorNode= ((JSONObject)current).entrySet().iterator();
            while(iteratorNode.hasNext()){
                Map.Entry<String,Object> entry=iteratorNode.next();
                if(entry.getKey().equals("index__")||entry.getKey().equals("parent__")){
                    iteratorNode.remove();
                }else if(entry.getValue() instanceof JSONObject){
                    removeIndex(current,entry.getValue());
                }else if(entry.getValue() instanceof JSONArray){
                    removeIndex(current,entry.getValue());
                }
            }
        }else if(current instanceof JSONArray){
            ((JSONArray)current).forEach(s->{
                 if(s instanceof JSONObject){
                     removeIndex(node,s);
                 }else if(s instanceof JSONArray){
                     removeIndex(node,s);
                 }
            });
        }
        return node;
    }

    public String getIndex(JSONObject node){
         return node.getString("index__");
    }

    public Object getObject(String key_,boolean isRemoveIndex){
        Object obj=indexMapping.get(getKey(key_));
        if(obj instanceof JSONObject){
            obj=clone(obj);
        }else if(obj instanceof JSONArray){
            obj=clone(obj);
        }
        if(isRemoveIndex){
            obj=removeIndex(obj,null);
        }
        return obj;
    }

    public Object getObject(String key_){
        return getObject(key_,true);
    }

    public Object clone(Object obj){
        return JSON.parse(JSON.toJSONString(obj));
    }

    public  String getJSONStr(){
        return JSON.toJSONString(rootJSON);
    }

}
